/**
 * The DNB API Javascript interface library.
 * Copyright 2012 Dun & Bradstreet Inc, All Rights Reserved
 *
 * The API is written using jQuery 1.6 and above and makes extensive use of
 * the Promise pattern.  Each call returns an instance of jQuery.Deferred.
 * So instead of passing a callback parameter to each async call, a Deferred
 * is returned that can be chained along as sort of stream of processes.
 *
 * Simply put, to use it in it's most basic form, you might call:
 *     DNB.Api.getCompany('114315195').done(your_callback);
 *
 * The Common Javascript Promise specification can be viewed here: 
 *     http://wiki.commonjs.org/wiki/Promises/A
 *
 * @fileOverview DNB API library.
 * @author <a href="mailto:jgentil@hoovers.com">Jon-Pierre Gentil</a>
 * @author <a href="mailto:opatel@hoovers.com">Ojas Patel</a>
 * @version 1.0
 * @requires jQuery
 * @requires jQuery.Deferred
 */

'use strict';

if(!window.console){window.console={}
}if(typeof window.console.log!=="function"){window.console.log=function(){}
}if(typeof window.console.warn!=="function"){window.console.warn=function(){}
}

/**
 *  @namespace The DNB Javascript Library.
 */
var DNB = DNB || {};

/**
 *  @namespace The DNB Direct API Wrapper Library
 */
DNB.Api = DNB.Api || {};

/**
 *  @namespace The DNB Direct Util Wrapper Library
 */
DNB.Util = DNB.Util || {};

/**
 *  @namespace The DNB Direct Util Wrapper Library
 */
DNB.Widgets = DNB.Widgets || {};

/** 
 * http://www.JSON.org/json2.js
 */
var JSON;JSON||(JSON={});
(function(){function k(a){return a<10?"0"+a:a}function o(a){p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function(a){var c=r[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function l(a,j){var c,d,h,m,g=e,f,b=j[a];b&&typeof b==="object"&&typeof b.toJSON==="function"&&(b=b.toJSON(a));typeof i==="function"&&(b=i.call(j,a,b));switch(typeof b){case "string":return o(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null";e+=n;f=[];if(Object.prototype.toString.apply(b)==="[object Array]"){m=b.length;for(c=0;c<m;c+=1)f[c]=l(c,b)||"null";h=f.length===0?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(i&&typeof i==="object"){m=i.length;for(c=0;c<m;c+=1)typeof i[c]==="string"&&(d=i[c],(h=l(d,b))&&f.push(o(d)+(e?": ":":")+h))}else for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=l(d,b))&&f.push(o(d)+(e?": ":":")+h);h=f.length===0?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+g+"}":"{"+f.join(",")+"}";e=g;return h}}if(typeof Date.prototype.toJSON!=="function")Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+k(this.getUTCMonth()+1)+"-"+k(this.getUTCDate())+"T"+k(this.getUTCHours())+":"+k(this.getUTCMinutes())+":"+k(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()};var q=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e,n,r={"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},i;if(typeof JSON.stringify!=="function")JSON.stringify=function(a,j,c){var d;n=e="";if(typeof c==="number")for(d=0;d<c;d+=1)n+=" ";else typeof c==="string"&&(n=c);if((i=j)&&typeof j!=="function"&&(typeof j!=="object"||typeof j.length!=="number"))throw Error("JSON.stringify");return l("",{"":a})};if(typeof JSON.parse!=="function")JSON.parse=function(a,e){function c(a,d){var g,f,b=a[d];if(b&&typeof b==="object")for(g in b)Object.prototype.hasOwnProperty.call(b,g)&&(f=c(b,g),f!==void 0?b[g]=f:delete b[g]);return e.call(a,d,b)}var d,a=String(a);q.lastIndex=0;q.test(a)&&(a=a.replace(q,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return d=eval("("+a+")"),typeof e==="function"?c({"":d},""):d;throw new SyntaxError("JSON.parse");}})();

(function() {
    DNB.Util.getScriptInfo = function(scriptName)
    {
        var rv = { src: '', baseUrl : '', options : {} };
        for (var i = 0, s = document.getElementsByTagName("script"), si; si = s[i]; i++) {
            var src = si.src.replace('.min','');
            if (src.match(scriptName))
            {
                rv.src = si.src;
                rv.baseUrl = src.substring(0,src.lastIndexOf('/')+1);
                var opts = null;
                try
                {
                    var json = si.innerText || si.textContent || si.innerHTML;
                    eval('opts = ' + json);
                    if (opts)
                        rv.options = opts;
                } catch(e) {}
            }
        }
        return rv;
    };
    DNB.Util.getUrlParam = function(name){
        var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
        if (!results) return null;
        return decodeURIComponent(results[1]) || '';
    };

    var scriptInfo = DNB.Util.getScriptInfo('dnbapi-2.0.0.js');

    /**
     * Configuration variables that can be overridden by the widget.
     */

    /**
     * JS_BASE is the URL prefix for where the DNB Javascript library lives.
     */
    DNB.Api.JS_BASE = scriptInfo.baseUrl;
    /**
     * API_BASE is the URL prefix for where the DNB Widget API lives.
     */
    DNB.Api.API_BASE = scriptInfo.options['API_BASE'] || 'rest/';



//    var uri = 'V2/organizations/799901301/products/DCP_ENH';

//    DNB.endpoint = '../../rest/proxy.php/';
    DNB.Api.get = function(uri)
    {
        var baseUrl = uri.match('^https?\://') ? uri : DNB.Api.API_BASE;


        // this is how we should be doing auth...
        var dfdAuthToken = $.Deferred();
        if (DNB.token)
        {
            console.log('token already cached');
            dfdAuthToken.resolve();
        }
        else
        {
            var setTokenFromCookie = function()
            {
                var token = DNB.Util.getCookieValue('dnb_direct_2_auth_token');
                if (token)
                {
                    console.log('got token from cookie');
                    DNB.token = token;
                }
            }

            setTokenFromCookie();
            if (DNB.token)
            {
                dfdAuthToken.resolve();
            }
            else
            {
                var opts =
                {
                    url: baseUrl + '_cookieAuth',
                    dataType: 'json',
                    success : function(resp, r)
                    {
                        setTokenFromCookie();
                        dfdAuthToken.resolve(resp);
                    },
                    error : function()
                    {
                        console.log(arguments);
                        console.log('could not get token');
                    }
                }
                $.ajax(opts);
            }
        }

        // but for demo, we want it to work this way...
        // no auth stuff in client = do it on server
        // - if server doesn't see auth, it'll get it but restrict access
        // - if server does have it, it'll work normally


        // todo: add global fail
//        return $.ajax({url : DNB.endpoint + uri, headers : { 'Authorization' : DNB.token }});

        var invoke = function(opts)
        {
//            var delay_sec = DNB.Api._pendingRequestCount/DNB.Api.CLIENT_MAX_QPS;
//            delay_sec += Math.pow(opts.tryCount, 1.2); // exponential delay
            var delay_sec = 0;
//            DNB.Api._pendingRequestCount++;
            setTimeout(function(){$.ajax(opts);}, 1000*delay_sec);
        }

        var dfd = $.Deferred();
        var opts =
        { 
            url: baseUrl + uri,
            headers: {"Authorization": DNB.token},
            dataType: 'json',
            tryCount: 0,
//            retryLimit: DNB.Api.RETRY_LIMIT,
            cache: true,
            success: function(resp, r)
            {
                var root_name = null;
                for (var k in resp)
                {
                    root_name = k;
                    break;
                }
                var root = resp[root_name];
                // TODO: look at http status code
                // per http://developer.dnb.com/docs/2.0/common/response-codes

                if (root.TransactionResult.ResultID == 'SC006')
                {
                    invoke(this);
//                    dfd.reject(resp.error.message);
                }
                else
                {
                    dfd.resolve(resp);
                }

            },
            error: function(_xhr, _textStatus, _errorThrown)
            {
                var resp = {};
                try {
                    resp = JSON.parse(_xhr.responseText);
                } catch(e) {
                    resp = {'errors': ['Unable to parse JSON: ' + _xhr.responseText]}
                }
                dfd.reject(resp);
                return;
                if (_xhr.status==403 && _xhr.responseText.match(/ /i))
                {

                }
                /*
                DNB.Api._pendingRequestCount--;
                if (_xhr.status==403 && _xhr.responseText.match(/over qps/i))
                {
                    this.tryCount++;
                    if (this.tryCount <= this.retryLimit) {
                        invoke(this);
                    } else {
                        dfd.reject(_xhr, _textStatus, _errorThrown);
                    }
                }
                else if (_xhr.status==403 && _xhr.responseText.match(/developer inactive/i))
                {
                    dfd.reject('Invalid Credentials - please ensure your API credentials are valid.');
                }
                else
                {
                    dfd.reject(_xhr, _textStatus, _errorThrown);
                }*/
                dfd.reject(_xhr, _textStatus, _errorThrown);
            }
        };
        dfdAuthToken.promise().then(function(token){
            invoke(opts);
        })
        return dfd.promise();
    }
})();


// http://jsfiddle.net/drosen/6XvqX/

    function coFormatResult(result) {
        var markup = '';
        markup += result.companyName + '';
//        markup += '<div>' + result.location + '</div>';
        markup += '<div>' + result.locationType + '</div>';
        console.log(markup);
        return markup;
    }

    function coFormatSelection(result) {
        console.log('sel', result);
        return result.companyName;
    }


/* Knockout integration */
    function KnockoutAsyncDataWrapper(uri, koSelf, selector, addl)
    {
//        this.koSelf = koSelf;
//        this.url = url;

        var vars = [];
        var self = this;
        this.stateNum = ko.observable(0);
        this.loading = ko.observable(false);
        this.loaded = ko.observable(false);
        this.error = ko.observable(false);
        for (var k in addl)
        {
            koSelf[k] = ko.observable(null);
        }

        this.data = ko.observable();

        this.is = function(state){
            if (state==='missing')
                st = 0;
            if (state==='loading')
                st = 1;
            if (state==='done')
                st = 2;
            return self.stateNum() == st;
            var st = 'missing';
            if (self.stateNum()===1)
                st = 'loading';
            if (self.stateNum()===2)
                st = 'done';
            console.log('st', st, self.stateNum(), state);
            return self.stateNum() == st;
        }



        var newUri = uri.replace(/\{([^}]+)\}/, function(a,b,c){
            if (typeof(koSelf[b])=='function')
            {
                vars.push(koSelf[b])
            }
            else
            {
                vars.push(function(){ return a });
            }
        });


        var getRestUriFunction = function(uri, vars)
        {
            var fn = function()
            {
                var idx = 0;
                var newUri = uri.replace(/\{([^}]+)\}/, function(a,b,c){
                    var n = idx;
                    idx++;
                    return vars[n]();
                });


                self.stateNum(1);
                self.loading(true);
                self.loaded(false);
                self.error(false);
                DNB.Api.get(newUri)
                    .always(function(){
                        self.loading(false);
                        self.loaded(false);
                    })
                    .done(function(data){
                        if (selector)
                        try {
                            data = eval('data.' + selector);
                        } catch(e)
                        {
                            console.warn('error applying selector', selector, 'on', data);
                            data = {};
                        }
                        var viewModel = ko.mapping.fromJS(data);
                        self.data(viewModel);
                        self.loaded(true);
                        self.error(false);
                        self.stateNum(2);
                        for (var k in addl)
                        {
                            var exp = addl[k];
                            try {
                                console.log('DATA', data, exp);
                                eval('koSelf[k]=data.' + exp);
                            } catch (e) {
                                console.error(e);
                            }
//                            koSelf[k] = ko.observable('YES!');
                        }

                    })
                    .fail(function(data){
                        var viewModel = ko.mapping.fromJS(data);
                        self.data(viewModel);
                        console.log(data);
//                        self.loaded(true);
                        self.error(true);
                    });

                return newUri;
            }



            return fn;
        }

        var fn = getRestUriFunction(uri, vars);
        this.requestUri = ko.computed(fn);
    }

    DNB.Api.ko = function(uri, viewModel, selector, addl)
    {
        return new KnockoutAsyncDataWrapper(uri, viewModel, selector, addl);
    }

    DNB.Api.koComputed = function(observable, expression, context)
    {
        var fn = function()
        {
            var rv = null;
            if (observable.loaded())
            {
                try {
                    eval('rv=this.' + exp);
                } catch (e) {
                    console.error(e);
                }
            }
            return rv;
        }
        return fn;
    }


/*
DNB.Api.ko = function(uri, viewModel, path) {
    var self = viewModel;

    var vars = [];

    var newUri = uri.replace(/\{([^}]+)\}/, function(a,b,c){
        if (typeof(self[b])=='function')
        {
            vars.push(self[b])
        }
        else
        {
            vars.push(function(){ return a });
        }
    });


    var getRestUriFunction = function(uri, vars)
    {
        var fn = function()
        {
            var idx = 0;
            var newUri = uri.replace(/\{([^}]+)\}/, function(a,b,c){
                var n = idx;
                idx++;
                return vars[n]();
            });
            return newUri;
        }
        return fn;
    }





    var fn = getRestUriFunction(uri, vars);

    var setComputedLoading = function(val, self)
    {
        return ko.computed(function(){
            return val;
        }, self);
    }

    var setComputed = function(val, self)
    {
        return ko.computed(val)
    }

    return ko.computed( function() {
        var ajaxUrl = fn();
        setComputedLoading(99, this);
//        self.assessment(1);

        DNB.Api.get(ajaxUrl)
            .always(function(){
                self.assessment(0)
            })
            .done(function(resp){
                var data = resp.OrderProductResponse.OrderProductResponseDetail.Product.Organization.Assessment;
                var viewModel = ko.mapping.fromJS(data);
                self.assessment(viewModel);
            })
            .fail(function(){

            });
//        return 1; // loading
    }, self );


    var fn = function()
    {
        var s_self = this;
        var exps = [];


        uri = uri.replace(/\{([^}]+)\}/,function(a,b,c){
            if (typeof(s_self[b])=='function')
                return s_self[b]();
//            console.log('match', a,b, c);
            return a;

        }); // "getThis"
        return uri;
    }
    return ko.computed(fn, self);
}
*/

ko.bindingHandlers['dnb-rest'] = {
    init: function(element, valueAccessor) {

console.log('dnb-rest', element, valueAccessor());
//        $(element).select2(opts);

//        $(element).select2(valueAccessor());

//        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
//            $(element).select2('destroy');
//        });
    },
    update: function(element) {


    }
};

ko.bindingHandlers['dnb-type-ahead'] = {
    init: function(element, valueAccessor) {

        var opts =
        {
            placeholder: "Search for a company",
            minimumInputLength: 2,
            xformatResult: coFormatResult,
            xformatSelection: coFormatSelection,
            xformatInputTooShort: function(term, minLength) {
                return '';

            },
            escapeMarkup: function (m) { return m; },
            ajax :
            {
                url: 'http://api.dnbdirectapps.com/typeahead/companies',
                dataType: 'json',
                cache: true,
                xxx_headers: function() {
                    console.log('get-headers');
                //    DNB._getToken();
                    return { "Authorization": DNB.token }
                },
                data: function(term, page)
                {
                    return {
                        q: term,
                        authorization : DNB.token
                    };
                },
                results : function(data, page)
                {
                    var results = $(data.resultSet.hit).map(function() {
                        return { id : this.companyResults.duns, text : this.companyResults.companyName }
//                        return this.companyResults;
//                        return { id : this['companyResults']['duns'], text : this['companyResults'].companyName }
                    }).toArray();
                    return { results : results };
                },
                quietMillis : 750,
/*
        success: function (data) {
            var results = $(data.resultSet.hit).map(function() { return { id : this['companyResults']['duns'], text : this['companyResults'].companyName }});
//                    console.log('data', results);
            query.callback({
                results: results //, text : 'companyName'
            });
        }
*/

            },
            initSelection: function(element, callback) {
                // the input tag has a value attribute preloaded that points to a preselected movie's id
                // this function resolves that id attribute to an object that select2 can render
                // using its formatResult renderer - that way the movie name is shown preselected
                var duns=$(element).val();
                if (duns!=="") {
                    $.ajax({
                        url: 'http://api.dnbdirectapps.com/typeahead/companies',
                        dataType: 'json',
                        cache: true,
                        data: {
                            q: duns,
                            authorization : DNB.token
                        }
                    }).done(function(data) {
                        var name = '';
                        try {
                            name = data.resultSet.hit[0].companyResults.companyName;
                        } catch(e) {

                        }
                        var rv = { id: duns, text : name };
                        callback(rv);
                    });
                }
            },
        };

        $(element).select2(opts);

//        $(element).select2(valueAccessor());

        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).select2('destroy');
        });
    },
    update: function(element) {
        $(element).trigger('change');
    }
};



DNB.Util.viabToNum = function(score)
{
    console.log('score', score);
    var min = 1;
    var max = 10;
    if (typeof(score)==='function')
        score = score();
    console.log(score);
    if (typeof(score)==='number')
    {
        // score is 1-9
        // 1->100%
        // 9->0%
        min = 1;
        max = 9;
        score = max - score;
    }
    if (typeof(score)==='string')
    {
        // A-G and H-M
        if (score>='A' && score<='G')
        {
            score = score.charCodeAt(0)
            min = 'A'.charCodeAt(0); // -> 100
            max = 'G'.charCodeAt(0); // -> 0
            score = max - score;
        }
        else if (score<='Z')
        {
            score = score.charCodeAt(0)
            min = 'A'.charCodeAt(0); // -> 100
            max = 'Z'.charCodeAt(0); // -> 0
            score = max - score;
        }
    }
    var range = max - min;
    score = score / range;
    return score;
}

DNB.Util.viabToPct = function(score)
{
    var num = DNB.Util.viabToNum(score);
    num *= 100;
    return num + '%';
}

DNB.Util.hexToRgb = function(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
//    return result ? {
//        r: parseInt(result[1], 16),
//        g: parseInt(result[2], 16),
//        b: parseInt(result[3], 16)
//    } : null;
    return result ? [
        parseInt(result[1], 16),
        parseInt(result[2], 16),
        parseInt(result[3], 16)
    ] : null;
}

DNB.Util.rgbToHex = function(rgb) {
    var componentToHex = function(c) {
        var hex = Math.round(c).toString(16);
        console.log('hex', c);
        return hex.length == 1 ? "0" + hex : hex;
    }
    return "#" + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
}

DNB.Util.blendRgbColor = function(hexColor1, hexColor2, blend)
{
    if (typeof(blend)==='undefined')
        blend = 0.5;
    var c1 = DNB.Util.hexToRgb(hexColor1);
    var c2 = DNB.Util.hexToRgb(hexColor2);
    var color = [];
    for(var i=0; i<3; i++)
    {
        var delta = c2[i] - c1[i];
        var c = c1[i] + blend * delta;
        color[i] = c; 
    }
    return DNB.Util.rgbToHex(color);
}

DNB.Util.getGradientColor = function(num, min, max)
{
    if (typeof(min)=='undefined')
        min = 0;
    if (typeof(max)=='undefined')
        max = 1;
    var range = max - min;
    num = (num - min) / range;
    var colors = ['#DA736D', '#f7b41f', '#30a567'];
    var colorNum = num * (colors.length-1);
    var diffBetweenColors = colorNum - Math.floor(colorNum);
    var num1 = Math.floor(colorNum);
    var num2 = Math.ceil(colorNum);
    var color1 = colors[num1];
    var color2 = colors[num2];
    var color = DNB.Util.blendRgbColor(color1, color2, diffBetweenColors);
    return color;
}

DNB.Util.viabToColor = function(score)
{
    var num = DNB.Util.viabToNum(score);
    return DNB.Util.getGradientColor(num);
}

DNB.Util.flatten = function(obj)
{
    var _transformName = function(arr)
    {
        var nm = arr[arr.length-1];
        if (nm=='$')
            nm = arr[arr.length-2];
        if (nm[0]=='@')
            nm = arr[arr.length-2] + ' ' + nm.substr(1);
        var nm2 = '';
        for (var i=0; i<nm.length; i++)
        {
            if (i>0 && nm[i].toUpperCase()==nm[i])
                nm2 += ' ';
            nm2 += nm[i];
        }
        nm2 = nm2.replace(/D N B/g, 'D&B');
        return nm2;
    }
    var _flatten = function(o, prefix)
    {
        if (typeof(prefix)=='undefined')
        {
            prefix = [];
        }
        var arr = [];
        for (var k in o)
        {
            if (!o.hasOwnProperty(k))
                continue;
            var nameParts = prefix.concat(k);
            if (typeof(o[k]) == 'object') {
                arr.push({
                    name: k,
                    fullName: nameParts,
                    friendlyName: _transformName(nameParts),
                    indent: nameParts.length-1,
                    value: null
                });
//                var addl = _flatten(o[k], nameParts);
                arr = arr.concat(_flatten(o[k], nameParts));
            }
            else
                arr.push({
                    name: k,
                    fullName: nameParts,
                    friendlyName: _transformName(nameParts),
                    indent: nameParts.length-1,
                    value: o[k]});
        }
        return arr;
    }

    var arr = _flatten(obj);
//    arr.sort(function(a,b) { return a.friendlyName==b.friendlyName?0:a.friendlyName<b.friendlyName?-1:1 })
    return arr;
}

DNB.Util.getCookieValue = function(name)
{
    var parts = document.cookie.split(';');
    for (var i=0; i<parts.length; i++)
    {
        var kv = parts[i].split('=');
        if (kv.length>=2 && decodeURIComponent(kv[0])===name)
            return decodeURIComponent(kv[1]);
    }
    return null;
}




ko.bindingHandlers.fadeVisible = {
    init: function(element, valueAccessor) {
        // Initially set the element to be instantly visible/hidden depending on the value
        var value = valueAccessor();
        $(element).toggle(ko.utils.unwrapObservable(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable
    },
    update: function(element, valueAccessor) {
        // Whenever the value subsequently changes, slowly fade the element in or out
        var value = valueAccessor();
        ko.utils.unwrapObservable(value) ? $(element).fadeIn() : $(element).fadeOut();
    }
};
 
 
